/**
 * @file
 * @brief Optimizer widget
 * @date 2020-10-20
 */
#pragma once

#include "state.h"

#include <QWidget>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

class QLabel;
class QProgressBar;
class QSpinBox;
class QToolButton;

/**
 * @struct OptimumValues
 * @brief Struct to store optimum weight values
 */
struct OptimumValues
{
    double powerWeight;                                     ///< power weight
    double powerFactor;                                     ///< power factor
    double xpWeight;                                        ///< xp weight
    double reduction;                                       ///< cost reduction factor
    double assetWeight;                                     ///< assets weight
    double assetFactor;                                     ///< assets factor

    /**
     * @brief Default WeightValues constructor
     */
    explicit OptimumValues()
        : powerWeight( 0 )
        , powerFactor( 0 )
        , xpWeight( 0 )
        , reduction( 0 )
        , assetWeight( 0 )
        , assetFactor( 0 ) {}
};

Q_DECLARE_METATYPE( OptimumValues )

/**
 * @class FinderThread
 * @brief QThread class for find optimal weight values
 */
class FinderThread : public QThread
{
    Q_OBJECT

public:
    /**
     * @brief Construct find object
     */
    explicit FinderThread( QObject * parent = nullptr );

    /**
     * @brief Finder desctructor
     */
    ~FinderThread();

public slots:
    /**
     * @brief Start optimal weight search
     * @param   data    player data as start point of search
     * @param   aCheck  spend resources every N minutes
     * @param   aSleep  time for sleep when player cannot spend resources
     */
    void findOptimum( const State data, int aCheck, int aSleep );

    /**
     * @brief abort running search
     */
    void stopSearch();

signals:
    /**
     * @brief Emitted on progress changed
     * @param   current     current progress
     * @param   total       total progress, when search will end
     */
    void progressChanged( int current, int total );

    /**
     * @brief Emitted when found better solution
     * @param   minutes     time to complete event
     * @param   weight      optimal weight values
     */
    void optimumFound( int minutes, const OptimumValues & weight );

protected:
    /**
     * @brief overrided QThread::run
     */
    void run() override;

private:
    int checkPeriod = 1;
    int sleepPeriod = 0;
    bool abort = false;
    bool stop = false;
    bool restart = false;

    QWaitCondition condition;
    QMutex mutex;

    State state;
};

/**
 * @class Finder
 * @brief QWidget for control search
 * 
 * There is 3 state on this widget:
 * 1. Control with two QSpinBox and 'Start' button. Press button will start search
 * 2. Progress bar with cancel button.
 * 3. Final text with "OK" button and filled progress bar
 */
class Finder : public QWidget
{
    Q_OBJECT
public:
    /**
     * @brief Construct finder object
     */
    explicit Finder( std::shared_ptr<State> aState, QWidget * parent = nullptr );

    /**
     * @brief update view based on player data
     */
    void updateData();
    
signals:
    /**
     * @brief emit on finding more better weight values
     */
    void optimumFound( const OptimumValues & weight );

private slots:
    void searchStart();
    void searchCancel();
    void searchFinish();
    void searchProgress( int current, int total );
    void findOptimum( int minutes, const OptimumValues & weight );

private:
    std::shared_ptr<State> playerData;
    QProgressBar * bar;
    QSpinBox * sleepBox;
    QSpinBox * checkBox;
    QToolButton * modeButton;
    QToolButton * searchButton;
    QToolButton * cancelButton;
    QToolButton * okButton;
    QLabel * text;

    bool isSearching = false;
    FinderThread thread;
};
